using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
using UnityEngine;

public class AssetBundleManager : Singleton<AssetBundleManager>
{
    //资源 关系 依赖 表 ，根据crc找
    protected Dictionary<uint, ResourceItem> ResourceItemDic = new Dictionary<uint, ResourceItem>();

    //储存已加载的ab包
    protected Dictionary<uint, AssetBundleItem> AssetBundleItemDic = new Dictionary<uint, AssetBundleItem>();

    //AssetBundleItem类对象池
    protected ClassObjectPool<AssetBundleItem> AssetBundleItemPool = ObjectManager.Instance.GetOrCreatClassPool<AssetBundleItem>(500);

    /// <summary>
    /// 初始 AB配置表
    /// </summary>
    /// <returns></returns>
    public bool LoadAssetBundleConfig()
    {
        ResourceItemDic.Clear();
        string configPath = Application.streamingAssetsPath + "/assetbundleconfig";
        AssetBundle configAb = AssetBundle.LoadFromFile(configPath);
        TextAsset textAsset = configAb.LoadAsset<TextAsset>("assetbundleconfig");//AssetBundleConfig
        if (textAsset == null)
        {
            Debug.LogError("AssetBundleConfig is no exist!");
            return false;
        }

        MemoryStream stream = new MemoryStream(textAsset.bytes);
        BinaryFormatter bf = new BinaryFormatter();
        AssetBundleConfig configSerialize = (AssetBundleConfig)bf.Deserialize(stream);
        stream.Close();

        foreach (var abBase in configSerialize.AbList)
        {
            ResourceItem item = new ResourceItem();
            item.Crc = abBase.Crc;
            item.AssetName = abBase.AssetName;
            item.AbName = abBase.AbName;
            item.DependAssetBundle = abBase.AbDepends;
            if (ResourceItemDic.ContainsKey(item.Crc))
            {
                Debug.LogError("重复crc ： " + item.AssetName + " ab包名 : " + item.AbName);
            }
            else
            {
                ResourceItemDic.Add(item.Crc,item);
            }
        }
        return true;
    }

    /// <summary>
    /// 根据路径 crc  加载 资源中间管理类  ResourceItem
    /// </summary>
    /// <param name="crc"></param>
    /// <returns></returns>
    public ResourceItem LoadResourceAssetBundle(uint crc)
    {
        ResourceItem item = null;

        if (!ResourceItemDic.TryGetValue(crc, out item) || item == null) 
        {
            Debug.LogError($"LoadResourceAssetBundle error：找不到crc {crc} ");
        }
        else if (item.AssetBundle != null)
        {
            return item;
        }
        else
        {
            item.AssetBundle = LoadAssetBundle(item.AbName);
            if (item.DependAssetBundle != null)
            {
                foreach (var abName in item.DependAssetBundle)
                {
                    LoadAssetBundle(abName);
                }
            }
        }
        return item;
    }

    /// <summary>
    /// 根据ab包名 加载AssetBundle  并生成ar添加 对应 ab包管理类 引用计数加
    /// </summary>
    /// <param name="name"></param>
    /// <returns></returns>
    private AssetBundle LoadAssetBundle(string name)
    {
        AssetBundleItem item = null;
        uint crc = CRC32.GetCRC32(name);

        if (!AssetBundleItemDic.TryGetValue(crc, out item)) 
        {
            string fuilPath = Application.streamingAssetsPath + "/" + name;

            AssetBundle assetBundle = null;

            if (File.Exists(fuilPath))
            {
                assetBundle = AssetBundle.LoadFromFile(fuilPath);
            }

            if (assetBundle == null)
            {
                Debug.LogError("Load AssetBundle error : " + fuilPath);
            }

            item = AssetBundleItemPool.Spawn(true);
            item.AssetBundle = assetBundle;
            item.RefCount++;
            AssetBundleItemDic.Add(crc, item);
        }
        else
        {
            item.RefCount++;
        }
        
        return item.AssetBundle;
    }

    /// <summary>
    /// 释放资源
    /// </summary>
    /// <param name="item"></param>
    public void ReleaseAsset(ResourceItem item)
    {
        if (item == null)
        {
            return;
        }

        if (item.DependAssetBundle != null && item.DependAssetBundle.Count > 0)
        {
            foreach (var abName in item.DependAssetBundle)    
            {
                UnLoadAssetBundle(abName);
            }
        }
        UnLoadAssetBundle(item.AssetName);
    }

    /// <summary>
    /// 卸载assetBundle
    /// </summary>
    /// <param name="name"></param>
    private void UnLoadAssetBundle(string name)
    {
        AssetBundleItem item = null;
        uint crc = CRC32.GetCRC32(name);
        if (AssetBundleItemDic.TryGetValue(crc,out item) && item != null)
        {
            item.RefCount--;
            if (item.RefCount <= 0 && item.AssetBundle != null)
            {
                item.AssetBundle.Unload(true);
                item.Rest();
                AssetBundleItemPool.Recycle(item);
                AssetBundleItemDic.Remove(crc);
            }
        }
    }

    /// <summary>
    /// 根据 crc 找 ResourceItem
    /// </summary>
    /// <param name="crc"></param>
    /// <returns></returns>
    public ResourceItem FindResourceItem(uint crc)
    {
        return ResourceItemDic[crc];
    }
    
}

public class AssetBundleItem
{
    public AssetBundle AssetBundle = null;
    public int RefCount;

    public void Rest()
    {
        AssetBundle = null;
        RefCount = 0;
    }
}

public class ResourceItem
{
    //资源路径crc
    public uint Crc;
   
    //资源 文件 名
    public string AssetName = string.Empty;

    //资源 所在 ab包 名
    public string AbName = string.Empty;

    //资源 依赖 ab包 名
    public List<string> DependAssetBundle = null;

    //----------------------------------------------------
    //资源加载完 的ab包
    public AssetBundle AssetBundle = null;
    
    //资源唯一标识
    public int Guid = 0;
    
    //资源对象
    public Object Obj = null;

    //资源最后使用的时间
    public float LastUseTime = 0.0f;
    
    //引用计数
    protected int MRefCount = 0;

    public int RefCount
    {
        get => MRefCount;
        set
        {
            MRefCount = value;
            if (MRefCount < 0)
            {
                Debug.LogError("RefCount < 0" + MRefCount + "," + (Obj != null ? Obj.name : "name is null"));
            }
        }
    }
}